package i18n

import (
	"errors"
	"strings"

	"golang.org/x/text/language"
	"golang.org/x/text/language/display"
)

var (
	ErrLangNameInvalid      = errors.New("langName is invalid")
	ErrLangNameEmpty        = errors.New("langName is empty")
	ErrLangNameShort        = errors.New("langName too short")
	ErrLangNameNotSupported = errors.New("langName is not supported")
)

// ParseLangName 将 i18n 语言名称转换为标准化的格式，例如：
//   - en => en
//   - english => en
//   - zh => zh
//   - chinese => zh
//   - zh_cn => zh-CN
//   - zh-hans => zh-Hans
func ParseLangName(langName string) (string, error) {
	langName = strings.TrimSpace(langName)
	if langName == "" {
		return "", ErrLangNameEmpty
	}
	if len(langName) < 2 {
		return "", ErrLangNameShort
	}
	if strings.Contains(langName, "_") {
		langName = strings.ReplaceAll(langName, "_", "-")
	}
	// 不区分大小写, 忽略大小写差异
	langName = strings.ToLower(langName)
	// 通过 langNameTagsCache 预定义的常用语言名称映射表, 快速处理常见语言名称
	if v, ok := langNameTagsCache[langName]; ok {
		return v.String(), nil
	}
	// 通过 language.Parse 处理其他语言名称
	if tag, err := language.Parse(langName); err == nil && tag != language.Und {
		langNameTagsCache[langName] = tag
		return tag.String(), nil
	}
	return "", ErrLangNameInvalid
}

// ParseLangTag 将 i18n 语言名称转换为标准库 language.Tag，例如：
//   - en =>  language.English
//   - english =>  language.English
//   - en-US => language.AmericanEnglish
//   - zh =>  language.Chinese
//   - chinese => language.Chinese
//   - zh-cn => zh-CN 非Golang内置语言Tag
//   - zh-hans => language.SimplifiedChinese
func ParseLangTag(langName string) (language.Tag, error) {
	langName = strings.TrimSpace(langName)
	if langName == "" {
		return language.Und, ErrLangNameEmpty
	}
	if len(langName) < 2 {
		return language.Und, ErrLangNameShort
	}
	if strings.Contains(langName, "_") {
		langName = strings.ReplaceAll(langName, "_", "-")
	}
	// 不区分大小写, 忽略大小写差异
	langName = strings.ToLower(langName)
	// 通过 langNameTagsCache 预定义的常用语言名称映射表, 快速处理常见语言名称
	if v, ok := langNameTagsCache[langName]; ok {
		return v, nil
	}
	// 通过 language.Parse 处理其他语言名称
	if tag, err := language.Parse(langName); err == nil && tag != language.Und {
		langNameTagsCache[langName] = tag
		return tag, nil
	}
	return language.Und, ErrLangNameInvalid
}

// langNameTagsCache 预定义的常用语言名称映射表
var (
	langNameTagsCache map[string]language.Tag
	// 200多个i18n国际化地区语言码名称
	langNamesList = []string{
		"en", "en-AU", "en-BZ", "en-CA", "en-IE", "en-JM", "en-NZ", "en-PH", "en-ZA", "en-TT", "en-GB", "en-US", "en-ZW",
		// "en-CB" are not supported by language.Parse
		"zh", "zh-CN", "zh-HK", "zh-MO", "zh-SG", "zh-TW",
		"zh-Hans", "zh-Hant", "zh-Hant-HK", "zh-Hant-MO", "zh-Hant-TW",
		// "zh-CHS", "zh-CHT" are not supported by language.Parse
		"af", "af-ZA",
		"sq", "sq-AL",
		"ar", "ar-DZ", "ar-BH", "ar-EG", "ar-IQ", "ar-JO", "ar-KW", "ar-LB", "ar-LY", "ar-MA", "ar-OM", "ar-QA", "ar-SA", "ar-SY", "ar-TN", "ar-AE", "ar-YE",
		"hy", "hy-AM",
		"az", // "az-AZ-Cyrl", "az-AZ-Latn" are not supported by language.Parse
		"eu", "eu-ES",
		"be", "be-BY",
		"bg", "bg-BG",
		"ca", "ca-ES",
		"hr", "hr-HR", "cs", "cs-CZ", "da", "da-DK",
		"div", "div-MV",
		"nl", "nl-BE", "nl-NL",
		"et", "et-EE",
		"fo", "fo-FO", "fa", "fa-IR", "fi", "fi-FI",
		"fr", "fr-FR", "fr-BE", "fr-CA", "fr-LU", "fr-MC", "fr-CH",
		"gl", "gl-ES",
		"ka", "ka-GE",
		"de", "de-AT", "de-DE", "de-LI", "de-LU", "de-CH",
		"el", "el-GR",
		"gu", "gu-IN",
		"he", "he-IL",
		"hi", "hi-IN",
		"hu", "hu-HU",
		"is", "is-IS",
		"id", "id-ID",
		"it", "it-IT", "it-CH",
		"ja", "ja-JP",
		"kn", "kn-IN",
		"kk", "kk-KZ",
		"kok", "kok-IN",
		"ko", "ko-KR",
		"ky", "ky-KZ",
		"lv", "lv-LV",
		"lt", "lt-LT",
		"mk", "mk-MK",
		"ms", "ms-BN", "ms-MY",
		"mr", "mr-IN",
		"mn", "mn-MN",
		"no", "nb-NO", "nn-NO",
		"pl", "pl-PL",
		"pt", "pt-BR", "pt-PT",
		"pa", "pa-IN",
		"ro", "ro-RO",
		"ru", "ru-RU",
		"sa", "sa-IN",
		// "sr-SP-Cyrl", "sr-SP-Latn"  are not supported by language.Parse
		"sk", "sk-SK",
		"sl", "sl-SI",
		"es", "es-AR", "es-BO", "es-CL", "es-CO", "es-CR", "es-DO", "es-EC", "es-SV", "es-GT",
		"es-HN", "es-MX", "es-NI", "es-PA", "es-PY", "es-PE", "es-PR", "es-ES", "es-UY", "es-VE",
		"sw", "sw-KE",
		"sv", "sv-FI", "sv-SE",
		"syr", "syr-SY",
		"ta", "ta-IN",
		"tt", "tt-RU",
		"te", "te-IN",
		"th", "th-TH",
		"tr", "tr-TR",
		"uk", "uk-UA",
		"ur", "ur-PK",
		"uz", // "uz-UZ-Cyrl", "uz-UZ-Latn" are not supported by language.Parse
		"vi", "vi-VN",
	}
)

func init() {
	langNameTagsCache = make(map[string]language.Tag)
	for _, name := range langNamesList {
		if tag, err := language.Parse(name); err == nil && tag != language.Und {
			name = strings.ToLower(tag.String())
			if _, ok := langNameTagsCache[name]; !ok {
				langNameTagsCache[name] = tag
			}
			name = strings.ToLower(display.English.Tags().Name(tag))
			if _, ok := langNameTagsCache[name]; !ok {
				langNameTagsCache[name] = tag
			}
			name = strings.ToLower(display.Self.Name(tag))
			if _, ok := langNameTagsCache[name]; !ok {
				langNameTagsCache[name] = tag
			}
		}
	}
}
